<!-- 菜单管理 -->
<template>
  <div class="menu-manage">
    <div class="menu-manage-header">
      <div class="menu-manage-header-left">
        <a-button type="dashed" status="danger" :size="'small'" @click="deleteCheckedMenu(checkedMenuTree)">
          <template #icon>
            <icon-delete />
          </template>
        </a-button>
        <a-button status="success" :size="'small'" style="margin-left: 10px"
          @click="openAddMenuModal('add')">新增</a-button>
      </div>
      <div class="menu-manage-header-right">

        <a-button type="primary" :size="'small'" @click="saveMenu"> 保存 </a-button>
      </div>
    </div>
    <a-tree :blockNode="true" :checkable="true" :data="(MenuTreeData as any)" draggable
      v-model:checked-keys="checkedMenuTree" @drop="onDrop" :fieldNames="{
        key: 'menuId',
        title: 'menuName',
        children: 'children'
      }" checkedStrategy="parent">
      <template #title="nodeData">
        <div class="menu-title">
          <p class="menu-icon">
            <img :src="[foldPng, pagePng][nodeData.type]" />
          </p>
          <p @dblclick="nodeData.editable = true" v-if="!nodeData.editable">
            {{ nodeData.menuName }}
          </p>
          <p style="display: flex" v-else>
            <a-input :style="{ width: '320px' }" v-model="nodeData.ProvisionalName" placeholder="Please enter something"
              allow-clear @blur="unSaveMenuName(nodeData)">
              <template #suffix>
                <span class="save-text" @click="saveMenuName(nodeData)">保存</span>
              </template>
            </a-input>
          </p>
        </div>
      </template>
      <template #extra="nodeData">
        <div class="extra-content">
          <div class="is_forbidden">
            <a-button type="text" status="success" v-if="nodeData.isForbidden == 1"
              @click="changeMenuState(nodeData, 0)">开启</a-button>

            <a-button type="text" status="warning" v-else @click="changeMenuState(nodeData, 1)">禁用</a-button>
          </div>
          <div class="delete">
            <a-button type="text" status="danger" @click="deleteCheckedMenu([nodeData.menuId])">删除</a-button>
          </div>
          <div class="edit">
            <a-button type="text" @click="() => openAddMenuModal('edit', nodeData)">编辑</a-button>
          </div>

          <div class="add" style="width: 30px; text-align: center">
            <IconPlus style="color: #52b46e" v-if="nodeData.type == 0"
              @click="() => openAddMenuModal('add', nodeData)" />
          </div>
        </div>
      </template>
    </a-tree>
  </div>

  <addMenuDialog :menu-data="menuModalData" v-model:visible="addMenuDialogVisible" :all-menu-data="MenuTreeData"
    :modalType="menuModalType" @submit="getMenuModalData"></addMenuDialog>
</template>

<script setup lang="ts">
import { ref, reactive, toRefs, onMounted } from 'vue'
import type { TreeNodeData } from '@arco-design/web-vue/es/tree/interface'
import MenuApi from '@/api/system/menu/MenuApi'
import type { Reactive, Ref } from 'vue'
import addMenuDialog from '@/components/system/menu/addMenuDialog.vue'
import { cloneDeep } from 'lodash'
import foldPng from '@/assets/images/fold.png'
import pagePng from '@/assets/images/page.png'
import { Message, Modal } from '@arco-design/web-vue'

type MenuTreeDataType = UserType.MenuInfo & {
  editable: boolean
  ProvisionalName: string
  children: Array<MenuTreeDataType> | []
}

let MenuTreeData: Ref<Array<MenuTreeDataType>> = ref([] as any)
let checkedMenuTree = ref<string[]>([])

getMenuData()

function getMenuData() {
  MenuApi.getAllMenuTreeList().then(res => {
    if (res.code == 200) {
      MenuTreeData.value = formatMenuData<MenuTreeDataType>(res.data, menuItem => {
        return {
          ...menuItem,
          editable: false, //当前menu是否可编辑
          ProvisionalName: menuItem.menuName //当前menu的临时名称
        }
      })
      sortMenu(MenuTreeData.value)
    }
  })
}

//格式化符合当前tree结构的数据
function formatMenuData<K>(data: Array<any>, callback: (_: any) => K): Array<K> {
  return data.map((item: any) => {
    if (item.children && item.children.length > 0) {
      item.children = formatMenuData(item.children as any, callback)
    }
    return callback(item)
  })
}

//拖拽方法
function onDrop({
  dragNode,
  dropNode,
  dropPosition
}: {
  dragNode: any
  dropNode: any
  dropPosition: number
}) {
  const data = MenuTreeData.value
  if (dropNode.type != 0 && dropPosition == 0) {
    //不是菜单节点 则不允许拖拽
    return
  }

  //找到原来的位置并删除原来的节点
  loopFindMenu(data, dragNode.menuId, (_, index, arr) => {
    arr.splice(index, 1)
  })


  //找到拖拽放置的目标位置并插入节点
  if (dropPosition === 0) {  //该拖拽的目标终点是否为某个目录

    dragNode.parentId = dropNode.menuId
    loopFindMenu(data, dropNode.menuId, item => {
     
      item.children = item.children || []
      item.children.push(dragNode)
      sortMenu(MenuTreeData.value)
    })
  } else { //拖拽目标终点不是某个目录
    dragNode.parentId = dropNode.parentId
    loopFindMenu(data, dropNode.menuId, (_, index, arr) => {
      let spliceIndex=dropPosition<0?index:index+1 //找到插入的位置
      arr.splice(dropPosition < 0 ? index : index + 1, 0, dragNode)//插入该节点

      let afterMenu = arr[spliceIndex+1] //找到插入的位置的下一个菜单
   
      let level = afterMenu?afterMenu.level+1:0 //根据前一个菜单层级设置当前菜单层级
      dragNode.level=level
      arr.forEach((item,index)=>{
        if(index<spliceIndex){
          item.level=level+1
        }
      })
      sortMenu(MenuTreeData.value)
    })
  }

  disabledMenu(MenuTreeData.value) //若父级菜单被禁用则批量禁用子级菜单
}

//根据菜单id递归查找指定菜单
function loopFindMenu(
  data: Array<MenuTreeDataType>,
  menuId: string,
  callback: (item: MenuTreeDataType, index: number, arr: any[]) => void
) {
  data.some((item: MenuTreeDataType, index: number, arr: any[]) => {
    if (item.menuId === menuId) {
      callback(item, index, arr)
      return true
    }
    if (item.children) {
      return loopFindMenu(item.children, menuId, callback)
    }
    return false
  })
}



//菜单排序
function sortMenu(menuTreeData:Reactive<Array<MenuTreeDataType>>) {
  menuTreeData.sort((a,b)=>{
    return b.level-a.level
  }).forEach((menuItem,index)=>{
    if(menuItem.children&&menuItem.children.length>0){
      sortMenu(menuItem.children)
    }
  })
}

//若父级菜单被禁用则批量禁用子级菜单
function disabledMenu(menuTreeData:Reactive<Array<MenuTreeDataType>>) {
  menuTreeData.forEach((item,index)=>{
    if(item.isForbidden==1){
      item.children.forEach((childItem)=>{
        childItem.isForbidden=1
      })
    }
    if(item.children&&item.children.length>0){
      disabledMenu(item.children)
    }
  })
}


//暂存菜单名称
function saveMenuName(nodeData: MenuTreeDataType) {
  nodeData.menuName = nodeData.ProvisionalName

  nodeData.editable = false
}
function unSaveMenuName(nodeData: MenuTreeDataType) {
  nodeData.editable = false
  nodeData.ProvisionalName = nodeData.menuName
}

//切换菜单状态
function changeMenuState(nodeData: MenuTreeDataType, isForbidden: 0 | 1) {
  if(isForbidden==0){
    nodeData.isForbidden=isForbidden
    return 
  }

  if(nodeData.children&&nodeData.children.length>0){
    Modal.warning({
    title: '禁用菜单提示',
    content: '该菜单下含有子级菜单，会导致子级菜单同时被禁用，是否继续？', 
    okText: '确定',
    cancelText: '取消',
    onOk: () => {
      nodeData.isForbidden = isForbidden
      disabledMenu(MenuTreeData.value) //批量禁用其父级被禁用的子级菜单
    }
  })
  }else{
    nodeData.isForbidden = isForbidden
  }

}

//-------------------------------------- 新增/编辑菜单弹框start---------------------------------
let addMenuDialogVisible = ref(false)
let menuModalData = ref<MenuTreeDataType>({
  menuName: '',
  menuId: '',
  parentId: '',
  isForbidden: 0,
  type: 0,
  routePath: '',
  editable: false,
  ProvisionalName: '',
  children: [],
  level: 0,
  icon: ""
})
let menuModalType = ref<'add' | 'edit'>('add') //默认弹框为新增

async function openAddMenuModal(type: 'add' | 'edit', menuData?: MenuTreeDataType) {
  let menuOriginData: MenuTreeDataType = {
    menuName: '',
    menuId: '',
    parentId: '',
    isForbidden: 0,
    type: 0,
    routePath: '',
    editable: false,
    ProvisionalName: '',
    children: [],
    level: 0,
    icon: ""
  }

  switch (type) {
    case 'add':
      MenuApi.getRandomMenuId().then(res => {
        if (res.code != 200) {
          Message.error(res.msg)
          return
        }
        menuOriginData.menuId = res.data

        if (menuData) {
          menuOriginData.parentId = menuData.menuId
        }
        menuModalData.value = cloneDeep(menuOriginData)
        menuModalType.value = 'add'
        addMenuDialogVisible.value = true
      })

      break
    case 'edit':
      menuModalType.value = 'edit'
      addMenuDialogVisible.value = true
      menuModalData.value = cloneDeep(menuData as MenuTreeDataType)
  }
}

function getMenuModalData(data: MenuTreeDataType, type: 'add' | 'edit') {
  data.ProvisionalName = data.menuName
  if (type == 'edit') {
    //找到之前菜单所在的位置删除
    loopFindMenu(
      MenuTreeData.value,
      data.menuId,
      (item: MenuTreeDataType, index: number, arr: any[]) => {
        arr.splice(index, 1)
      }
    )
  }

  //找到目前菜单的父级菜单 并插入该菜单
  let parentMenu: MenuTreeDataType | null = null
  loopFindMenu(MenuTreeData.value, data.parentId, item => {
    parentMenu = item
  })
  if (parentMenu) {
    ; (parentMenu as MenuTreeDataType).children = (parentMenu as MenuTreeDataType).children || []
      ; (parentMenu as MenuTreeDataType).children.push(data)
  } else {
    MenuTreeData.value.push(data)
  }
  sortMenu(MenuTreeData.value) //菜单排序
  disabledMenu(MenuTreeData.value) //批量禁用其父级被禁用的子级菜单

}

//-------------------------------------- 新增/编辑菜单弹框end---------------------------------
//删除指定的菜单数据
function deleteCheckedMenu(delMenuList: Array<string>) {
  if (delMenuList.length == 0) {
    Message.warning('请选择要删除的菜单')
    return
  }
  delMenuList.forEach(menuId => {
    loopFindMenu(MenuTreeData.value, menuId, (item: MenuTreeDataType, index: number, arr: any[]) => {
      if (arr[index].children.length > 0) {
        Modal.warning({
          title: '提示',
          content: `${arr[index].menuName}下存在子菜单，确认删除吗？`,
          okText: '确认',
          onOk: () => {
            arr.splice(index, 1)
            return
          },
          cancelText: '取消',
          onCancel: () => {
            return
          }
        })

      } else {
        arr.splice(index, 1)
      }
    })
  })
}


//调用接口保存修改后的菜单数据
function saveMenu() {
  let MenuListData = formatMenuData<UserType.MenuInfo>(
    cloneDeep(MenuTreeData.value),
    menuItem => {
      return {
        menuId: menuItem.menuId,
        menuName: menuItem.menuName,
        routePath: menuItem.routePath,
        type: menuItem.type,
        isForbidden: menuItem.isForbidden,
        parentId: menuItem.parentId,
        level: menuItem.level,
        icon: menuItem.icon,
        children: menuItem.children || []
      }
    }
  )

  MenuApi.saveMenuTreeData({
    menusTreeData: MenuListData
  }).then(res => {
    if (res.code == 200) {

      Message.success('保存成功')
      getMenuData()
    }
  })
}
</script>
<style scoped lang="scss">
.menu-manage-header {
  width: 100%;
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
}

.menu-manage-header-left {
  width: auto;
  display: flex;
  align-items: center;
}

.menu-manage-header-right {
  flex: 1;
  display: flex;
  justify-content: flex-end;
}

.menu-title {
  display: flex;
  width: 100%;
  p{
    margin-bottom: 0px !important;
  }
  align-items: center;
}

.menu-icon {
  margin-right: 10px;
  display: flex;
  align-items: center;

  img {
    width: 20px;
  }
}

.menu-manage {
  width: 100%;
  padding: 15px;
  box-sizing: border-box;
}

.extra-content {
  display: flex;
  align-items: center;
}

.save-text {
  cursor: pointer;
  color: #ddb42a;
}

.is_forbidden {}
</style>
